﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Gendarme.Framework;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Gendarme;
using ICSharpCode.NRefactory;
using Commander.Core.Refactorings.Modifiers;

namespace Commander.Core.AI {
  public class Population : List<Encoding> {
    public Population() {

    }
    public Population(Population newGen) {
      this.Clear();
      this.AddRange(newGen);
    }

    public void EvaluateAll() {
      foreach (var item in this) {
        item.EvaluateFitness();
      }
    }

    public Encoding Fittest {
      get {
        return this.OrderByDescending(e => e.Fitness).First();
      }
    }

  }

  [Serializable]
  public class Encoding : List<EncodingItem>, IComparable {
    public int InitialSmellCount { get; set; }
    public double InitialQMoodScore { get; set; }
    public double InitialCyclomaticComplexity { get; set; }
    public double InitialHalsteadScore { get; set; }
    public string FilePath { get; set; }
    public double Fitness { get; private set; }
    public List<Defect> InitialDefects { get; internal set; }
    private string refactoredContent = null;

    public Encoding() {

    }

    public Encoding(List<Defect> defects) {
      Reset(defects);

    }

    public Encoding(System.Collections.ObjectModel.Collection<Defect> defects)
      : this(defects.ToList()) {
    }

    public Encoding Clone() {
      var clone = new Encoding {
        FilePath = this.FilePath,
        InitialCyclomaticComplexity = this.InitialCyclomaticComplexity,
        InitialHalsteadScore = this.InitialHalsteadScore,
        InitialQMoodScore = this.InitialQMoodScore,
        Fitness = this.Fitness,
        InitialDefects= this.InitialDefects
      };
      foreach (var item in this) {
        clone.Add(item);
      }
      return clone;
    }

    private void Reset(List<Defect> defects) {
      InitialDefects = defects;
      InitialSmellCount = defects.Count;
      this.Clear();
      foreach (var item in defects) {
        this.Add(new EncodingItem(item));
      }
    }

    private void Reset(System.Collections.ObjectModel.Collection<Defect> defects) {
      Reset(defects.ToList());
    }

    /// <summary>
    /// Calculte the fitness of the encoding
    /// </summary>
    /// <returns>fitness score of the encoding</returns>
    /// <remarks>side effect: changes the initial values</remarks>
    public void EvaluateFitness() {
      //Fitness Function = 0.7 * (Smell Eliminated / Smell Detected - Smell Introduced / Smell Eliminated) + 0.1 * del(QMOOD) + 0.1 * del(Cyclometic Complexity) + del (Halstead)

      ApplyRefactoring();
      Evaluate();
    }

    private void Evaluate() {
      var result = EncodingCompiler.CompileFile(this);
      if (result.HasSucceeded) {
        string[] args = new string[3];
        args[0] = "--set";
        args[1] = "smells";
        args[2] = result.OutputLocation;
        var defects = ConsoleRunner.CommanderExec(args);
        int defectDelta = defects.Count - InitialSmellCount;
        //if (defectDelta > 0) {//smells have been introduced
        //  Fitness = -defectDelta;
        //} else {//smells have been eleminated
        //  Fitness = defectDelta;
        //}

        Fitness = (defectDelta > 0) ? -defectDelta : defectDelta;
        //if (Fitness == 0) Fitness = int.MaxValue;
        //Reset(defects);
      } else {
        //throw new Exception("Could not compile the encoding");
        Fitness = int.MinValue;
      }
    }

    private void ApplyRefactoring() {

      //if (string.IsNullOrEmpty(refactoredContent)) {
      refactoredContent = File.ReadAllText(FilePath);
      //}
      for (int i = 0; i < Count; i++) {
        try {
          string newContent = ApplyRefactoring(this[i], refactoredContent);
          this[i].HasSucceeded = true;
          refactoredContent = newContent;
        }
        catch (Exception ex) {
          this[i].HasSucceeded = false;
        }
      }

    }

    private string ApplyRefactoring(EncodingItem item, string origiContent) {
      var parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(origiContent));
      parser.Parse();
      return NetWalkerCodeModifier.CreateModifier(item.CommanderExtra, parser, item.OriginalDefect.Rule).ApplyModification();
    }

    public int CompareTo(object obj) {
      var other = (Encoding)obj;
      return this.Fitness.CompareTo(other.Fitness);
    }

    public override string ToString() {
      if (string.IsNullOrEmpty(refactoredContent)) {
        return File.ReadAllText(FilePath);
      } else {
        return refactoredContent;
      }
    }

  }
  [Serializable]
  public class EncodingItem {
    public string RuleName { get; set; }
    public XElement CommanderExtra { get; set; }
    public bool? HasSucceeded { get; set; }

    private Defect defect;
    public Defect OriginalDefect {
      get { return defect; }
      private set { defect = value; }
    }

    public EncodingItem(Defect item) {
      // TODO: Complete member initialization
      this.defect = item;
      RuleName = item.Rule.Name;
      CommanderExtra = item.CommanderExtra;
    }

    public EncodingItem() {

    }

  }

  //public static class ObjectClone {
  //  public static T Clone<T>(this T RealObject) {
  //    using (Stream objectStream = new MemoryStream()) {
  //      IFormatter formatter = new BinaryFormatter();
  //      formatter.Serialize(objectStream, RealObject);
  //      objectStream.Seek(0, SeekOrigin.Begin);
  //      return (T)formatter.Deserialize(objectStream);
  //    }
  //  }
  //}
}


